今天要來把RV64I才有的指令做完。
RV64I
RV64I特有的指令大多是原先32bit的指令因為架構擴展到64bit,因此需要新增32bit的特別型態,例如說ADDI這道指令在32bit的架構是將相加結果sign-extension到32,但在64bit的架構下會sign-extension到64,這時如果我們想要將32bit的值相加,但最後結果要sign-extension到64就需要特別的指令。
舉例來說 我們以0x80000000+ 0x00000001,相加結果是 0x80000001。
以addi來說,這個值就是正的,可以直接放回暫存內,但以addiw來說這是一個負值,需要sign-extension後才能放回去暫存內。
又例如說向load/store指令在32bit下會有lhu, lbu但沒有lwu,這是因為讀出來32bit的值有沒有sign-extension結果相同,而64bit下則需要lhu指令。結合上述內容,RV64I特有的指令包含前面介紹過的9道ALU指令以及另外3道Load/Store指令,整理成下表。
指令 | 行為 |
---|---|
addw |
將暫存內的值相加後,以word(32bit)為單位sext到64bit後存回暫存內。 |
subw |
將暫存內的值相減後,以word(32bit)為單位sext到64bit後存回暫存內。 |
addiw |
將暫存內的值加上一個立即值後,以word(32bit)為單位sext到64bit後存回暫存內。 |
sllw |
將暫存內的值邏輯左移後,以word(32bit)為單位sext到64bit後存回暫存內。 |
slliw |
將暫存內的值邏輯左移後,以word(32bit)為單位sext到64bit後存回暫存內。 |
srlw |
將暫存內的值邏輯右移後,以word(32bit)為單位sext到64bit後存回暫存內。 |
srliw |
將暫存內的值邏輯右移後,以word(32bit)為單位sext到64bit後存回暫存內。 |
sraw |
將暫存內的值算數右移後,以word(32bit)為單位sext到64bit後存回暫存內。 |
sraiw |
將暫存內的值算數右移一個立即值後,以word(32bit)為單位sext到64bit後存回暫存內。 |
lwu |
將四個byte的值讀出,直接放到暫存內。 |
ld |
將八個byte的值讀出,放到暫存內。 |
sd |
將暫存的值從最低位開始寫八個byte到記憶體內。 |
RV64I測試程式實作
我們實作測試程式,可參考RV32I的測試程式寫法,但可以新增例如在add及addw會有不同答案的測資,例如前面提到的0x80000000 + 0x00000001,如下。
TEST(ISATESTSuite, ADDW0x80000000_0x1)
{
ALISS::reg[10] = 0x0;
ALISS::reg[11] = 0x80000000;
ALISS::reg[12] = 0x1;
uint32_t insn = 0x00c5853b; // addw a0 a1 a2
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[10], 0xFFFFFFFF80000001); //3 + 7 = 10;
}
TEST(ISATESTSuite, ADDIW0x80000000_0x1)
{
ALISS::reg[10] = 0x0;
ALISS::reg[11] = 0x80000000;
uint32_t insn = 0x0015851b; // addiw a0 a1 1
ALISS::ID_EX_WB(insn);
EXPECT_EQ(ALISS::reg[10], 0xFFFFFFFF80000001); //3 + 7 = 10;
}
RV64I指令實作
而指令行為的實作上則更簡單,以ALU而言,只要將原先的add,addi等指令的結果取low32bit後再sign extension到64bit即可,如下。
case 0x3b: //OP-32bit
{
uint64_t rd = ((insn >> 7) & 0x1f);
uint64_t rs1 = ((insn >> 15) & 0x1f);
uint64_t rs2 = ((insn >> 20) & 0x1f);
switch ((insn >> 12) & 7)
{
case 0x0: //ADDW or SUBW
{
switch ((insn >> 25) & 0x7f)
{
case 0x0 : //ADDW
{
reg[rd] = sext((reg[rs1] + reg[rs2]) & 0xffffffff,32);
break;
}
case 0x20 : //SUBW
{
reg[rd] = sext((reg[rs1] - reg[rs2]) & 0xffffffff,32);
break;
}
default:
{
printf("Illegal instruction");
printf("%x\n",insn);
break;
}
}
break;
}
case 0x1: //SLLW
{
reg[rd] = sext((reg[rs1] << reg[rs2]) & 0xffffffff,32);
break;
}
case 0x5: //SRLW or SRAW
{
switch ((insn >> 25) & 0x7f)
{
case 0x0 : //SRLW
{
reg[rd] = sext((reg[rs1] >> reg[rs2]) & 0xffffffff,32);
break;
}
case 0x20 : //SRAW
{
reg[rd] = sext(((int64_t)reg[rs1] >> reg[rs2]) & 0xffffffff,32);
break;
}
default:
{
printf("Illegal instruction");
printf("%x\n",insn);
break;
}
}
break;
}
default:
{
printf("Illegal instruction");
printf("%x\n",insn);
break;
}
}
break;
}
至於Load/Store指令的32bit版,則只要擴充原本的get_mem_w等function為get_mem_d版本即可。
碎碎念 : 沒意外的話明天就能把I-extension收尾,把ecall/ebreak/wfi/mret以及前面漏掉的lui,auipc完成,並進入M-extension的世界。